// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: develop dispatcher
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// feature: support lookup writer/reader proxy info with cmd-tool
// feature: Crop code for in-process communication
// ------------------------------------------------------------------

#include <edds/rtps/writer/ReaderLocator.h>

#include <rtps/participant/RTPSParticipantImpl.h>

#include <history/CacheChange.h>
#include <edds/rtps/reader/RTPSReader.h>
#include <edds/rtps/common/LocatorListComparisons.hpp>

#include "rtps/RTPSDomainImpl.hpp"

namespace evbs {
namespace ertps {
namespace rtps {

ReaderLocator::ReaderLocator(RTPSWriter* owner, size_t max_unicast_locators, size_t max_multicast_locators)
    : owner_(owner),
      participant_owner_(owner->getRTPSParticipant()),
      general_locator_info_(max_unicast_locators, max_multicast_locators),
      async_locator_info_(max_unicast_locators, max_multicast_locators),
      is_local_reader_(false),
      local_reader_(nullptr),
      guid_prefix_as_vector_(1u),
      guid_as_vector_(1u) {}

ReaderLocator::~ReaderLocator() {}

bool ReaderLocator::start(const GUID_t& remote_guid, const ResourceLimitedVector<Locator_t>& unicast_locators,
                          const ResourceLimitedVector<Locator_t>& multicast_locators) {
    if (general_locator_info_.remote_guid == c_Guid_Unknown) {
        assert(c_Guid_Unknown == async_locator_info_.remote_guid);
        guid_as_vector_.at(0U) = remote_guid;
        guid_prefix_as_vector_.at(0U) = remote_guid.guidPrefix;
        general_locator_info_.remote_guid = remote_guid;
        async_locator_info_.remote_guid = remote_guid;
#ifdef INTRA_PROCESS_ENABLE
        is_local_reader_ = RTPSDomainImpl::should_intraprocess_between(owner_->getGuid(), remote_guid);
#endif
        local_reader_ = nullptr;

        if (!is_local_reader_) {
            if (owner_->getSendMulti() || unicast_locators.size() == 1) {
                general_locator_info_.unicast = unicast_locators;
                async_locator_info_.unicast = unicast_locators;
            } else if (unicast_locators.size() > 1) {
                const Locator_t& loc = participant_owner_->find_optimal_remote_locator(unicast_locators);

                general_locator_info_.unicast.push_back(loc);
                async_locator_info_.unicast.push_back(loc);
            }
            general_locator_info_.multicast = multicast_locators;
            async_locator_info_.multicast = multicast_locators;
        }

        general_locator_info_.reset();
        general_locator_info_.enable(true);
        async_locator_info_.reset();
        async_locator_info_.enable(true);
        evbs::ertps::rtps::Time_t::now(start_time_);
        return true;
    }

    return false;
}

bool ReaderLocator::update(const ResourceLimitedVector<Locator_t>& unicast_locators,
                           const ResourceLimitedVector<Locator_t>& multicast_locators) {
    bool ret_val = false;

    if (!(general_locator_info_.multicast == multicast_locators)) {
        general_locator_info_.multicast = multicast_locators;
        async_locator_info_.multicast = multicast_locators;
        ret_val = true;
    }
    if (!is_local_reader_) {
        if (owner_->getSendMulti() || unicast_locators.size() == 1) {
            if (!(general_locator_info_.unicast == unicast_locators)) {
                general_locator_info_.unicast = unicast_locators;
                async_locator_info_.unicast = unicast_locators;
                ret_val = true;
            }
        } else if (unicast_locators.size() > 1) {
            const Locator_t& loc = participant_owner_->find_optimal_remote_locator(unicast_locators);

            if (general_locator_info_.unicast.size() == 0 || !(*general_locator_info_.unicast.begin() == loc)) {
                general_locator_info_.unicast.clear();
                async_locator_info_.unicast.clear();
                general_locator_info_.unicast.push_back(loc);
                async_locator_info_.unicast.push_back(loc);
                ret_val = true;
            }
        }
    }

    if (ret_val) {
        general_locator_info_.reset();
        general_locator_info_.enable(true);
        async_locator_info_.reset();
        async_locator_info_.enable(true);
    }

    return ret_val;
}

bool ReaderLocator::stop(const GUID_t& remote_guid) {
    if (general_locator_info_.remote_guid == remote_guid) {
        assert(remote_guid == async_locator_info_.remote_guid);
        stop();
        return true;
    }

    return false;
}

void ReaderLocator::stop() {
    general_locator_info_.enable(false);
    general_locator_info_.reset();
    general_locator_info_.multicast.clear();
    general_locator_info_.unicast.clear();
    general_locator_info_.remote_guid = c_Guid_Unknown;
    async_locator_info_.enable(false);
    async_locator_info_.reset();
    async_locator_info_.multicast.clear();
    async_locator_info_.unicast.clear();
    async_locator_info_.remote_guid = c_Guid_Unknown;
    guid_as_vector_.at(0U) = c_Guid_Unknown;
    guid_prefix_as_vector_.at(0U) = c_GuidPrefix_Unknown;
    is_local_reader_ = false;
    local_reader_ = nullptr;
    start_time_ = vbsutil::c_TimeInvalid;
}

bool ReaderLocator::send(CDRMessage_t* message) const {
    if ((general_locator_info_.remote_guid != c_Guid_Unknown) && (!is_local_reader_)) {
        if (general_locator_info_.unicast.size() > 0U) {
            return participant_owner_->sendSync(message, owner_->getGuid(),
                                                Locators(general_locator_info_.unicast.begin()),
                                                Locators(general_locator_info_.unicast.end()), owner_->getSendMulti());
        } else {
            return participant_owner_->sendSync(
                message, owner_->getGuid(), Locators(general_locator_info_.multicast.begin()),
                Locators(general_locator_info_.multicast.end()), owner_->getSendMulti());
        }
    }

    return true;
}

RTPSReader* ReaderLocator::local_reader() {
    if (!local_reader_) {
        local_reader_ = RTPSDomainImpl::find_local_reader(general_locator_info_.remote_guid);
    }
    return local_reader_;
}

} /* namespace rtps */
} /* namespace ertps */
} /* namespace evbs */
